package com.runa.rccprotocol.analysis;

import com.runa.monitor.comm.dto.newComm.AffiliatePanelVO;
import com.runa.monitor.comm.dto.newComm.HotMeterVO;
import com.runa.monitor.comm.dto.newComm.PanelVO;
import com.runa.monitor.comm.dto.newComm.ValveVO;
import com.runa.persistence.RepositoryServicePersis;
import com.runa.protocol.analysis.MetaBufferAnalysis;
import com.runa.protocol.enums.ByteOrder;
import com.runa.protocol.util.ByteUtil;
import com.runa.protocol.util.DigitalUtil;
import com.runa.rccprotocol.dto.equdata.*;
import com.runa.rccprotocol.dto.runa.RunaConcenAffirmDeny;
import com.runa.rccprotocol.dto.runa.RunaConcenResponseData;
import lombok.extern.slf4j.Slf4j;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author diandian
 * @Date 2022/5/13 16:56
 */
@Slf4j
public class HuanXiangEquAnalysis extends MetaBufferAnalysis<byte[]> {
    private DateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");

    public static Map<String, EquData.EquDataType> valveInfo = new HashMap<>();

    private String netEquNo;

    @Override
    public int fill(byte[] data, int length, String netEquNo) {
        this.netEquNo = netEquNo;
        this.responseData = null;
        int begin = -1;
        for (int i = 0; i < data.length; i++) {
            int start = data[i] & 0xff;
            if (start == 0x68) {
                begin = i;
                break;
            }
        }
        if (begin == -1) {
            //如果没有匹配到报文头，就试试是不是modbus协议
            return handleModbusMeterData(data, length);
        }

        //判断数据长度
        int dataLen = data[begin + 10] & 0xff;

        if (length < (begin + 11 + dataLen + 2)) {
            return handleModbusMeterData(data, length);
        }

        //校验
        byte check = data[begin + 11 + dataLen];
        byte sum = ByteUtil.sum(data, begin, 11 + dataLen);
        if (check != sum) {
            return handleModbusMeterData(data, length);
        }

        int index = begin;
        ++index;
        int equType = data[index] & 0xff;
        if (equType == 0x20) {
            handleMeterData(data, begin, index, length);
        } else if (equType == 0x46) {
            handleValveData(data, begin, index, length);
        }

        return length - (begin + 11 + dataLen + 2);
    }

    private int handleModbusMeterData(byte[] data, int len) {
        int index = 0;
        int serial = data[index] & 0xff;
        String serialNo = String.valueOf(serial);
        int length = serialNo.length();
        StringBuilder serialNoBuf = new StringBuilder(serialNo);
        for (int i = 0; i < 14 - length; i++) {
            serialNoBuf.insert(0, "0");
        }

        HotMeterVO meterData = new HotMeterVO();
        meterData.setMeterSerial(serialNoBuf.toString());

        index += 2;
        int dataLen = data[index] & 0xff;
        int surplus = len - (dataLen + 5);
        if (surplus < 0) {
            return -1;
        }
        ++index;

        //校验
        int checkValue = DigitalUtil.calcCrc16(data, 0, dataLen + 3);
        Integer value = ByteUtil.byteToUShort(data, dataLen + 3, ByteOrder.BA);
        if (checkValue != value) {
            return surplus;
        }

        double flow = ByteUtil.byteToInt(data, index, ByteOrder.CDAB) * 0.001;
        index += 6;
        meterData.setFlow((float) flow);

        double heat = ByteUtil.byteToInt(data, index, ByteOrder.CDAB) * 10 * 0.001;
        index += 6;
        meterData.setPower((float) heat);

        /**
         * （0C13 对应单位为 0.001m3，含义为累积流量为 12345.678m3，0C14 对应单位为 0.01m3,含义为累积流量为 123456.78 m3，0x0C15 对应单位为
         * 0.1m3,含义为累积流量为 1234567.8 m3，0x0C16 对应单位为 1m3,含义为累积流量为 12345678 m3）
         */
        Integer accFlow = ByteUtil.byteToInt(data, index, ByteOrder.CDAB);
        index += 4;
        short accFlowUnit = ByteUtil.byteToShort(data, index, ByteOrder.AB);
        index += 2;
        double accFlow_d = 0;
        switch (accFlowUnit) {
            case 0x0C13:
                accFlow_d = accFlow * 0.001;
                break;
            case 0x0C14:
                accFlow_d = accFlow * 0.01;
                break;
            case 0x0C15:
                accFlow_d = accFlow * 0.1;
                break;
            case 0x0C16:
                accFlow_d = accFlow;
                break;
            default:
                break;
        }
        meterData.setAccFlow((float) accFlow_d);

        /**
         * 寄存器10、11为累积热量寄存器，32位整数，寄存12为累积热量的单位寄存器0C03对应单位为0.001KWh，含义为累积热量为12345.678KWh
         * 0C04-0C05 省却0C06对应单位为KWh,含义为累积热量为12345678KWh 0x0C07对应单位为10KWh,含义为累积热量为12345678*10KWh
         */
        Integer accHeat = ByteUtil.byteToInt(data, index, ByteOrder.CDAB);
        index += 4;
        short accHeatUnit = ByteUtil.byteToShort(data, index, ByteOrder.AB);
        index += 2;
        double accHeat_d = accHeatUnit;
        switch (accHeatUnit) {
            case 0x0C03:
                accHeat_d = accHeat * 0.001;
                break;
            case 0x0C06:
                accHeat_d = accHeat;
                break;
            case 0x0C07:
                accHeat_d = accHeat * 10;
                break;
            default:
                break;
        }
        meterData.setAccHeat((float) accHeat_d);

        /**
         * 寄存器13、4为累积冷量寄存器，32位整数，寄存器15为累积冷量的单位寄存器0C03对应单位为0.001KWh，含义为累积冷量12345.678KWh
         * 0C04-0C05 省却 0C06对应单位为KWh,含义为累积冷量为12345678KWh
         * 0x0C07对应单位为10KWh, 含义为累积冷量为12345678*10KW
         */
        Integer accCloud = ByteUtil.byteToInt(data, index, ByteOrder.CDAB);
        index += 4;
        short accCloudUnit = ByteUtil.byteToShort(data, index, ByteOrder.AB);
        index += 2;
        double accCloud_d = accCloud;
        switch (accCloudUnit) {
            case 0x0C03:
                accCloud_d = accCloud * 0.001;
                break;
            case 0x0C06:
                accCloud_d = accCloud;
                break;
            case 0x0C07:
                accCloud_d = accCloud * 10;
                break;
            default:
                break;
        }

        //寄存器16、17对应为进水温度寄存器，32位整数，（00000A9B对应十进制为2715、含义为进水温度27.15℃）
        double inTemp = ByteUtil.byteToInt(data, index, ByteOrder.CDAB) * 0.01;
        index += 4;
        meterData.setSupplyTemp((float) inTemp);

        //寄存器18、19对应为回水温度寄存器，32位整数，（00000BC4对应十进制为3012、含义为回水温度30.12℃）
        double outTemp = ByteUtil.byteToInt(data, index, ByteOrder.CDAB) * 0.01;
        index += 4;
        meterData.setReturnTemp((float) outTemp);

        //全0表示正常，BIT5表示电源故障，BIT2表示流量故障，BIT1表示温度故障，举例，如0x0020,表示电源故障，0x0004表示流量故障，0x0002表示温度故障；
//        int staus = data[index + 1] & 0xff;
        meterData.setStatus(ByteUtil.byteToHexString(data, index, 2));
        meterData.setFaultCode(heatMeterFaultsOfModbus(ByteUtil.byteToHexString(data, index, 2)));
        index += 2;

        //寄存器21、22对应为累积工作时间寄存器，32位整数，（00BC614E含义为累积工作时间12345678小时）
        Integer workTime = ByteUtil.byteToInt(data, index, ByteOrder.CDAB);
        index += 4;
        meterData.setWorkTime(workTime);

        //寄存器地址23~32: 0x0000 未定义
        index += 10;

        //寄存器地址33: 0x5678
        index += 2;

        //寄存器地址34: 0x1234
        index += 2;

        //寄存器33、34对应为188协议表地址寄存器，地址为BCD码对应表号为12345678

        //寄存器地址35：0x0078 modbus地址, 其中ModBus地址为最低字节0x78

        //寄存器地址36: 0x0001 仪表类型，0001为热表

        //寄存器地址37: 0x0001 波特率2400bps

        //寄存器地址38: 0x0000 版本号, 不支持寄存器地址>38

        //CRC高字节：0xxxxx
        //CRC低字节：0xxxxx 校验方式CRC-16/ModBus,x16+x15+x2+1
        RunaConcenResponseData runaConcenResponseData = new RunaConcenResponseData();
        runaConcenResponseData.addRepData(meterData);
        responseData = runaConcenResponseData;
        return surplus;
    }

    static public List heatMeterFaultsOfModbus(String status) {
        int hexCode = Integer.parseInt(status, 16);
        ArrayList<String> list = new ArrayList<>();
        if ((hexCode >> 5 & 1) == 1) {
//            faults.append("电池欠压,");
            list.add("电池欠压");
        }
        if ((hexCode >> 2 & 1) == 1) {
//            faults.append("进水回水故障,");
            list.add("进水回水故障");
        }
        if ((hexCode >> 1 & 1) == 1) {
//            faults.append("温度异常,");
            list.add("温度异常");
        }
        return list;
    }


    private void handleValveData(byte[] data, int begin, int index, int len) {
        ++index;
        String addresss = ByteUtil.byteToHexStringFlip(data, index, 7);

        index += 7;
        int ctrCode = data[index] & 0xff;
        ++index;

        //数据域长度
        ++index;

        //数据标识DI，
        String dataType = ByteUtil.byteToHexStringFlip(data, index, 2);
        index += 2;
        if ("9021".equals(dataType)) {
            //序号SER，序列号，
            ++index;
            valveCurrentData(data, index, addresss);
        } else if ("A030".equals(dataType)) {
            //控制阀门开关响应值
            //序号SER，序列号，
            ++index;
            //状态 BIT0-1：阀门状态（00 开，01 关，11 异常）
            //BIT4：1 强制控制，0 自由控制
            int stateValue = ByteUtil.getByBits(data[index], 0, 2);
            boolean state = stateValue != 2;
            this.responseData = new RunaConcenAffirmDeny(null, state);
        } else if ("D148".equals(dataType)) {
            //抄阀门开度反馈值
            //序号SER，序列号，
            ++index;
            Short open = ByteUtil.byteToShort(data, index, ByteOrder.BA);
            ValveVO valveData = new ValveVO();
            valveData.setValveSerial(addresss);
//            valveData.setOutOpenValve(Integer.valueOf(open));
            RunaConcenResponseData responseData = new RunaConcenResponseData();
            responseData.addRepData(valveData);
            this.responseData = responseData;
        } else if ("A048".equals(dataType)) {
            //控制阀门开度响应值
            this.responseData = new RunaConcenAffirmDeny(null, true);
        }
    }

    private void valveCurrentData(byte[] data, int index, String addresss) {
        EquData.EquDataType equDataType = valveInfo.get(addresss);

        //用户设定温度
        double setTem = ByteUtil.byteToShort(data, index, ByteOrder.BA) * 0.1;
        index += 2;

        //当前室内温度
        double roomTem = ByteUtil.byteToShort(data, index, ByteOrder.BA) * 0.1;
        index += 2;

        //周期平均温度
        double avgTem = ByteUtil.byteToShort(data, index, ByteOrder.BA) * 0.1;
        index += 2;

        //累积工作时间
        Integer workTime = ByteUtil.byte3ToInt(data, index, true);
        index += 3;

        //累积开阀时间
        Integer openTime = ByteUtil.byte3ToInt(data, index, true);
        index += 3;

        //状态1  BIT0-1：阀门状态（00 开，01 关，10 开度，11 异常）
        byte state1 = data[index];
        int byBits = ByteUtil.getByBits(state1, 0, 2);
        boolean state = byBits != 1;
        ++index;

        //状态1   BIT0-1：预留 BIT2：AC 掉电（0 正常，1 掉电） BIT3：拆机 （0 正常，1 拆机） BIT4-7：预留
        byte state2 = data[index];
        ++index;

        Short valveOpen = ByteUtil.byteToShort(data, index, ByteOrder.BA);
        index += 2;

        //阀门模式
        byte valveMode = data[index];
        index++;

        //报警周期内最高温度
//        ByteUtil.byteToShort(data, index, ByteOrder.BA);
        index += 2;

        //报警周期内最低温度
//        ByteUtil.byteToShort(data, index, ByteOrder.BA);
        index += 2;

        //报警时的温度
//        ByteUtil.byteToShort(data, index, ByteOrder.BA);
        index += 2;

        //报警时间
//        ByteUtil.byteToHexString(data, index, 4);
        index += 4;

        //进水温度
        double inTemp = ByteUtil.byteToShort(data, index, ByteOrder.BA) * 0.01;
        index += 2;

        double outTemp = ByteUtil.byteToShort(data, index, ByteOrder.BA) * 0.01;
//        index += 2;

        RunaConcenResponseData responseData = new RunaConcenResponseData();
//        if (EquData.EquDataType.VALVE.equals(equDataType)) {
        ValveVO valveVO = new ValveVO();
//            ValveData valveData = new ValveData();
        valveVO.setValveSerial(addresss);
//            valveVO.setSetTemperature((float) setTem);
//            ValveData.Panel panel = new ValveData.Panel(0, 0, (float) roomTem, (short) 0);
//            panel.setSerial(addresss);
//            valveData.setPanel0(panel);
        valveVO.setOpenTime(Float.valueOf(openTime));
//            valveVO.setSwitchState(state);
        valveVO.setActOpen(Float.valueOf(valveOpen));
        valveVO.setSupplyTemp((float) inTemp);
        valveVO.setReturnTemp((float) outTemp);
        valveVO.setReadTime(new Date());
        valveVO.setValveDate(valveVO.getReadTime());
        AffiliatePanelVO panel = new AffiliatePanelVO(0, 0, (float) roomTem, (short) 0);
        panel.setReadTime(valveVO.getReadTime());
        panel.setSerial(addresss);
        //温度带面板使用map
//            HashMap<String, Object> stringObjectHashMap = new HashMap<>();
//            stringObjectHashMap.put("valve",valveVO);
//            stringObjectHashMap.put("panel",panel);
        try {
            panel.setId(RepositoryServicePersis.getCommPanelByNetIdAndSerial(
                    RepositoryServicePersis.getCommNetEquipmentByNetSerial(this.netEquNo).getId(), valveVO.getValveSerial()).getId());
            valveVO.getPanelVOList().add(panel);
        } catch (Exception e) {
            log.error("户阀挂面板无面板数据---valveData:{}", valveVO, e);
        }
        responseData.addRepData(valveVO);

//        } else if (EquData.EquDataType.PUBLIC_UNIT.equals(equDataType)) {
//            ValveVO valveData = new ValveVO();
//            valveData.setValveSerial(addresss);
////            valveData.setCurrOpenValve(valveOpen);
//            valveData.setActOpen(Float.valueOf(valveOpen));
//            valveData.setSupplyTemp((float) inTemp);
//            valveData.setReturnTemp((float) outTemp);
//            valveData.setReadTime(new Date());
//            responseData.addRepData(valveData);
//        }
        this.responseData = responseData;
    }


    private void handleMeterData(byte[] data, int begin, int index, int len) {
        HotMeterVO meterData = new HotMeterVO();
        ++index;
        String addresss = ByteUtil.bcd2str(data, begin + 2, 7, false);
        meterData.setMeterSerial(addresss);
        index += 7;

        int ctrCode = data[index] & 0xff;
        ++index;

        //数据域长度
        ++index;

        //数据标识DI，序号SER，序列号，
        index += 3;

        //结算日热量
        double dayHeat = ByteUtil.bcdToInt(data, index, 4, false) * 0.01;
        //单位，占一个字节
        index += 5;

        float accHeat = (float) (Integer.parseInt(ByteUtil.byteToHexStringFlip(data, index, 4)) * 0.01);
        index += 4;
        meterData.setAccHeat(RunaDeviceAnalysis.toKwh(accHeat, data[index++] & 0xff));
        float power = (float) (Integer.parseInt(ByteUtil.byteToHexStringFlip(data, index, 4)) * 0.01);
        index += 4;
        meterData.setPower(RunaDeviceAnalysis.toKw(power, data[index++] & 0xff));
        float flow = (float) (Integer.parseInt(ByteUtil.byteToHexStringFlip(data, index, 4)) * 0.0001);
        index += 4;
        meterData.setFlow(RunaDeviceAnalysis.toM3h(flow, data[index++] & 0xff));
        float accFlow = (float) (Integer.parseInt(ByteUtil.byteToHexStringFlip(data, index, 4)) * 0.01);
        index += 4;
        meterData.setAccFlow(RunaDeviceAnalysis.toM3(accFlow, data[index++]));
        meterData.setSupplyTemp((float) (Integer.parseInt(ByteUtil.byteToHexStringFlip(data, index, 3)) * 0.01));
        index += 3;
        meterData.setReturnTemp((float) (Integer.parseInt(ByteUtil.byteToHexStringFlip(data, index, 3)) * 0.01));
        index += 3;
        meterData.setWorkTime(Integer.parseInt(ByteUtil.byteToHexStringFlip(data, index, 3)));
        index += 3;

//        //当前热量
//        meterData.setAccHeat((float) (ByteUtil.bcdToInt(data, index, 4, false) * 0.01));
//        index += 5;
//
//        //热功率
//        meterData.setPower((float) (ByteUtil.bcdToInt(data, index, 4, false) * 0.01));
//        index += 5;
//
//        //流量
//        meterData.setFlow((float) (ByteUtil.bcdToInt(data, index, 4, false) * 0.01));
//        index += 5;
//
//        //累积流量
//        meterData.setAccFlow((float) (ByteUtil.bcdToInt(data, index, 4, false) * 0.01));
//        index += 5;
//
//        //供水温度
//        meterData.setSupplyTemp((float) (ByteUtil.bcdToInt(data, index, 3, false) * 0.01));
//        index += 3;
//
//        //回水温度
//        meterData.setReturnTemp((float) (ByteUtil.bcdToInt(data, index, 3, false) * 0.01));
//        index += 3;
//
//        //累积工作时间
//        meterData.setWorkTime((ByteUtil.bcdToInt(data, index, 3, false)));
//        index += 3;

        //实时时间
        try {
            //设备时间
            int equTimeLength = 6;
            String equTime = ByteUtil.bcd2str(data, index, equTimeLength, false);
            meterData.setMeterDate(new SimpleDateFormat("yyMMddHHmmss").parse(equTime));
//            meterData.setMeterDate(parse);
        } catch (ParseException e) {
            
        }
        index += 7;
        meterData.setStatus(ByteUtil.byteToHexString(data, index, 2));
        meterData.setFaultCode(heatMeterFaults(ByteUtil.byteToHexString(data, index, 2)));
        RunaConcenResponseData runaConcenResponseData = new RunaConcenResponseData();
        runaConcenResponseData.addRepData(meterData);
        responseData = runaConcenResponseData;

    }

    static List heatMeterFaults(String hexCode) {
        int hexCodeOne = Integer.parseInt(hexCode.substring(0, 2), 16);
//        StringBuilder faults = new StringBuilder();
        ArrayList<String> list = new ArrayList<>();
        if ((hexCodeOne >> 2 & 1) == 1) {
//            faults.append("电池欠压,");
            list.add("电池欠压");
        }
//        if (faults.length() > 0) faults = newComm StringBuilder(faults.substring(0, faults.length() - 1));
        return list;
    }


    public static void main(String[] args) {
//        String s1 = heatMeterFaultsOfModbus("0002");


//        String s = "C03203004022348302000044C0A8049F8AEAFEFEFE682095730621000000812E901F010006000005000600000500000000170000000035260000002C602000752000000000380917131023203000CD16BDD0      ";
        String s = "C03203004021075602000045C0A8049F8AEAFEFEFEFE682010151010000000812E901F010400000005197159010522050000173028000035017408002C4763003847007889013533091912232000009C16FFD0        ";
        byte[] bytes = ByteUtil.hexStringToByte(s);
//        Integer aShort = ByteUtil.byteToUShort(bytes, bytes.length - 2, ByteOrder.BA);
//        System.out.println(aShort + "----" + DigitalUtil.calcCrc16(bytes, 0, bytes.length - 2));

        HuanXiangEquAnalysis equAnalysis = new HuanXiangEquAnalysis();
        HuanXiangFmuAnalysis huanXiangFmuAnalysis = new HuanXiangFmuAnalysis(equAnalysis);
        huanXiangFmuAnalysis.fill(bytes, bytes.length, "");

    }

}
